package com.icedata.sika.core

import com.icedata.sika.bridge.SikaEvent
import com.icedata.sika.bridge.SikaGroupEvent
import com.icedata.sika.bridge.SikaUserEvent
import com.icedata.sika.database.DatabaseOperator
import com.icedata.sika.util.Logger
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import net.mamoe.mirai.event.GlobalEventChannel
import net.mamoe.mirai.event.events.BotOnlineEvent
import net.mamoe.mirai.event.events.GroupMessageEvent
import net.mamoe.mirai.event.events.MessageEvent
import net.mamoe.mirai.event.events.UserMessageEvent
import java.io.File
import java.sql.Connection
import java.sql.DriverManager
import java.sql.PreparedStatement
import java.sql.ResultSet
import java.util.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
import kotlin.concurrent.timerTask
import kotlin.coroutines.CoroutineContext

/**
 * Sika对象
 *
 */
class Sika {
    /**
     * Sika的配置类
     */
    val config: SikaConfig

    var closeFlag: Boolean = true

    /**
     * 使用配置文件构造Sika
     * @param configFile 配置文件路径（相对or绝对路径）
     * @throws SikaConfigNotFoundException 配置文件不存在时抛出
     */
    @Throws(SikaConfigNotFoundException::class)
    constructor(configFile: String = "config/sika.config") {
        with(File(configFile)) {
            if (!exists()) {
                throw SikaConfigNotFoundException()
            }
            config = Json.decodeFromString(readText())
        }
    }


    /**
     * 使用配置类构造Sika
     * @param config 配置对象
     * @see SikaConfig
     */
    constructor(config: SikaConfig) {
        this.config = config
    }

    /**
     * 使用构造器构造Sika
     */
    constructor(buildConfig: SikaConfig.() -> Unit) {
        this.config = SikaConfig().apply {
            buildConfig()
        }
    }

    /**
     * 初始化Sika
     */
    fun init() {

        GlobalEventChannel.subscribeAlways<BotOnlineEvent> {
            println("Sika:B/BotOnline")
        }

        threadPool = Executors.newScheduledThreadPool(config.threadPoolSIze)

        parseMessageChannel = Channel(config.channelSize)

        commandChannel = Channel(config.channelSize)

        exceptionChannel = Channel(config.channelSize)

        if (config.jdbc.isNotEmpty()) {
            Class.forName(config.jdbc)
            databaseConnect =
                DriverManager.getConnection(config.databaseUrl, config.databaseUser, config.databasePassword)
        }

        val coroutineExceptionHandler =
            CoroutineExceptionHandler { _, t ->
                println(Thread.currentThread())
                t.printStackTrace()
            }

        //5个解析协程
        repeat(config.processorSize) {
            GlobalScope.launch(coroutineExceptionHandler) {
                while (closeFlag) {
                    parseProcessorBus.broadcast(parseMessageChannel.receive())
                }
            }
        }

        //5个处理协程
        repeat(config.processorSize) {
            GlobalScope.launch(coroutineExceptionHandler) {
                while (closeFlag) {
                    commandProcessorBus.broadcast(commandChannel.receive())
                }
            }
        }

        GlobalScope.launch(coroutineExceptionHandler) {
            while (closeFlag) {
                exceptionProcessorBus.broadcast(exceptionChannel.receive())
            }
        }
    }

    /**
     * 预检测总线
     */
    private val preProcessorBus: PreProcessorBus = PreProcessorBus(this)

    /**
     * 通过预检测的消息通道
     */
    internal lateinit var parseMessageChannel: Channel<SikaEvent>

    /**
     * 解析总线
     */
    private val parseProcessorBus: ParseProcessorBus = ParseProcessorBus(this)

    /**
     * 执行通道
     */
    lateinit var commandChannel: Channel<Command>

    /**
     * 执行总线
     */
    private val commandProcessorBus: CommandProcessorBus = CommandProcessorBus(this)

    /**
     * 异常通道
     */
    private lateinit var exceptionChannel: Channel<Exception>

    /**
     * 异常处理器链
     */
    private val exceptionProcessorBus: ExceptionProcessorBus = ExceptionProcessorBus(this)

    /**
     * 定时线程池
     */
    private lateinit var threadPool: ScheduledExecutorService

    /**
     * sika执行的协程上下文
     */
    private val context: CoroutineContext = GlobalScope.coroutineContext

    /**
     * sika的数据库连接，可以为空
     */
    private var databaseConnect: Connection? = null

    /**
     * sika的数据库操作方法，不初始化数据库时为空
     */
    val databaseOperator: DatabaseOperator? by lazy {
        databaseConnect?.let {
            DatabaseOperator(it)
        }
    }

    /**
     * 广播一个异常
     */
    fun throwException(e: Exception) {
        exceptionChannel.offer(e)
    }

    /**
     * 向Sika广播一条消息
     */
    fun newMessage(messageEvent: MessageEvent) {
        val event: SikaEvent? = when (messageEvent) {
            is UserMessageEvent -> SikaUserEvent(messageEvent)
            is GroupMessageEvent -> SikaGroupEvent(messageEvent)
            else -> null
        }

        event?.let {
            Logger.logEvent(it)
            preProcessorBus.broadcast(event)
        } ?: println(messageEvent::class.java)
    }

    /**
     * 挂载一个预处理器
     *
     * @param processor PreProcessor 预处理器
     */
    fun mountPreProcessor(processor: PreProcessor) {
        preProcessorBus.mount(processor)
    }

    fun mountPreProcessorInProcess(processor: PreProcessor) {
        preProcessorBus.mountInProcess(processor)
    }

    /**
     * 挂载一个预处理器
     *
     * @param priority Int 预处理器优先级
     * @param function Function2<SikaEvent, Sika, Result> 预处理器方法
     * @return PreProcessor 返回构造的处理器
     */
    fun mountPreProcessor(priority: Int = 5000, function: (SikaEvent, Sika) -> PreProcessor.Result): PreProcessor {
        return PreProcessor(priority, function).apply {
            mountPreProcessor(this)
        }
    }

    fun mountPreProcessorInProcess(priority: Int = 5000, function: (SikaEvent, Sika) -> PreProcessor.Result): PreProcessor {
        return PreProcessor(priority, function).apply {
            mountPreProcessorInProcess(this)
        }
    }

    /**
     * 挂载一个解析器
     *
     * @param processor ParseProcessor 解析器
     */
    fun mountParseProcessor(processor: ParseProcessor) {
         parseProcessorBus.mount(processor)
    }

    fun mountParseProcessorInProcess(processor: ParseProcessor) {
        parseProcessorBus.mountInProcess(processor)
    }

    /**
     * 挂在一个执行器
     *
     * @param processor CommandProcessor 执行器
     */
    fun mountCommandProcessor(processor: CommandProcessor) {
        commandProcessorBus.mount(processor)
    }

    fun mountCommandProcessorInProcess(processor: CommandProcessor) {
        commandProcessorBus.mountInProcess(processor)
    }

    /**
     * 挂载一个异常处理器
     *
     * @param processor ExceptionProcessor 异常处理器
     */
    fun mountExceptionProcessor(processor: ExceptionProcessor) {
        exceptionProcessorBus.mount(processor)
    }

    fun mountExceptionProcessorInProcess(processor: ExceptionProcessor) {
        exceptionProcessorBus.mountInProcess(processor)
    }

    /**
     * 挂载一个异常处理器
     *
     * @param priority Int 处理器优先级
     * @param function Function2<Exception, Sika, Unit> 处理器方法
     * @return ExceptionProcessor 返回构造的处理器
     */
    fun mountExceptionProcessor(priority: Int = 5000, function: (Exception, Sika) -> Unit): ExceptionProcessor {
        return ExceptionProcessor(priority, function).apply {
            exceptionProcessorBus.mount(this)
        }
    }

    fun mountExceptionProcessorInProcess(priority: Int = 5000, function: (Exception, Sika) -> Unit): ExceptionProcessor {
        return ExceptionProcessor(priority, function).apply {
            exceptionProcessorBus.mountInProcess(this)
        }
    }

    /**
     * 卸载预处理器
     *
     * @param processor PreProcessor 需要卸载的预处理器
     */
    fun unmountPreProcessor(processor: PreProcessor) {
        preProcessorBus.unmount(processor)
    }

    fun unmountPreProcessorInProcess(processor: PreProcessor) {
        preProcessorBus.unmountInProcess(processor)
    }

    /**
     * 卸载解析器
     *
     * @param processor ParseProcessor 需要卸载的解析器
     */
    fun unmountParseProcessor(processor: ParseProcessor) {
        parseProcessorBus.unmount(processor)
    }

    fun unmountParseProcessorInProcess(processor: ParseProcessor) {
        parseProcessorBus.unmountInProcess(processor)
    }

    /**
     * 卸载执行器
     *
     * @param processor CommandProcessor 需要卸载的执行器
     */
    fun unmountCommandProcessor(processor: CommandProcessor) {
        commandProcessorBus.unmount(processor)
    }

    fun unmountCommandProcessorInProcess(processor: CommandProcessor) {
        commandProcessorBus.unmountInProcess(processor)
    }

    /**
     * 卸载异常处理器
     *
     * @param processor ExceptionProcessor 需要卸载的异常处理器
     */
    fun unmountExceptionProcessor(processor: ExceptionProcessor) {
        exceptionProcessorBus.unmount(processor)
    }

    fun unmountExceptionProcessorInProcess(processor: ExceptionProcessor) {
        exceptionProcessorBus.unmountInProcess(processor)
    }

    /**
     * 注册一个延迟任务
     *
     * @param delay Long 间隔执行的时间
     * @param timeUnit TimeUnit 时间的单位
     * @param function Function1<Sika, Unit> 需要执行的方法
     */
    fun delay(delay: Long, timeUnit: TimeUnit = TimeUnit.MILLISECONDS, function: (Sika) -> Unit) {
        if (!threadPool.isShutdown)
            threadPool.schedule(Runnable { function(this) }, delay, timeUnit)
    }

    private val timerTaskList = ConcurrentHashMap<String, Timer>()

    fun schedule(period: Long, function: (Sika) -> Unit) {
        Timer().let {
            it.schedule(timerTask {
                function(this@Sika)
            }, 0, period)
            timerTaskList[it.hashCode().toString()] = it
        }
    }

    fun schedule(id: String, period: Long, function: (Sika) -> Unit) {
        Timer().let {
            it.schedule(timerTask {
                function(this@Sika)
            }, 0, period)
            timerTaskList[id] = it
        }
    }

    fun stopSchedule(id: String) {
        timerTaskList[id]!!.cancel()
        timerTaskList.remove(id)
    }

    /**
     * 往数据库中插入一个记录
     *
     * @param data T 记录，需要有[com.icedata.sika.database.Table]标签
     */
    inline fun <reified T : Any> databaseInsert(data: T) {
        databaseOperator!!.getDataOperator<T>().insert(data)
    }

    /**
     * 筛选一个记录
     *
     * @param primaryKey Any 主键
     * @return T 记录，需要有[com.icedata.sika.database.Table]标签
     */
    inline fun <reified T : Any> databaseSelect(primaryKey: Any): T {
        return databaseOperator!!.getDataOperator<T>().select(primaryKey)
    }

    /**
     * 数据库复制整个表
     *
     * @return LinkedList<T> 记录表，需要有[com.icedata.sika.database.Table]标签
     */
    inline fun <reified T : Any> databaseCopy(): LinkedList<T> {
        return databaseOperator!!.getDataOperator<T>().copy()
    }

    /**
     * 更新数据库一个记录
     *
     * @param data T 记录，需要有[com.icedata.sika.database.Table]标签
     */
    inline fun <reified T : Any> databaseUpdate(data: T) {
        databaseOperator!!.getDataOperator<T>().update(data)
    }

    /**
     * 删除一个记录
     *
     * @param primaryKey Any 记录，需要有[com.icedata.sika.database.Table]标签
     */
    inline fun <reified T : Any> databaseDelete(primaryKey: Any) {
        databaseOperator!!.getDataOperator<T>().delete(primaryKey)
    }

    /**
     * 获取数据库执行预编译接口
     *
     * @param sql String sql语句
     * @return PreparedStatement 预编译接口
     */
    fun getStatement(sql: String): PreparedStatement {
        return databaseConnect!!.prepareStatement(
            sql,
            ResultSet.TYPE_SCROLL_INSENSITIVE,
            ResultSet.CONCUR_READ_ONLY
        )
    }


    /**
     * 关闭Sika
     */
    fun close() {
        closeFlag = false

        threadPool.shutdownNow()

        context.cancel()

        databaseOperator?.closeOperator()
        databaseOperator?.close()

        timerTaskList.forEach { (_, it) ->
            it.cancel()
        }
        //TODO 部分运行中的协程终止
    }
}

/**
 * Sika配置文件不存在的异常
 */
class SikaConfigNotFoundException : Exception()


/**
 * Sika配置类
 */
@Serializable
data class SikaConfig(
    /**
     * Sika的名称
     */
    var name: String = "uname",
    /**
     * 用于Mirai交互的QQ账号
     */
    var qq: Long = 0,
    /**
     * QQ账号的密码
     */
    var password: String = "",
    /**
     * 设备设置文件地址
     */
    var devicePath: String = "",
    /**
     * 启动NET日志
     */
    var netLog: Boolean = false,
    /**
     * 启动Bot日志
     */
    var botLog: Boolean = true,
    /**
     * 接入设备协议
     */
    var protocol: String = "ANDROID-PHONE",
    /**
     * 自动重连接
     */
    var autoReconnect: Boolean = true,

    /**
     * 初始化协程数量
     */
    var processorSize: Int = 5,

    /**
     * 通道容量
     */
    var channelSize: Int = 20,

    /**
     * 线程池数量
     */
    var threadPoolSIze: Int = 4,

    /**
     * 数据库地址
     */
    var databaseUrl: String = "jdbc:sqlite:test.db",

    /**
     * 数据库驱动
     */
    var jdbc: String = "",

    /**
     * 数据库用户名
     */
    var databaseUser: String = "",

    /**
     * 数据库密码
     */
    var databasePassword: String = ""
)